using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Dimok.Data.Database;
using Dimok.Data.DataSources;
using Dimok.Exceptions;
using Dimok.Web.UI;
namespace Dimok.Web.UI {

	#region EventArgs Classes

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Additional information for grid view insert events. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class GridViewInsertEventArgs : CancelEventArgs {
		public IOrderedDictionary Keys = new OrderedDictionary();
		public IOrderedDictionary InsertingValues = new OrderedDictionary();
		public IOrderedDictionary InsertedValues = new OrderedDictionary();

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Default constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public GridViewInsertEventArgs():base() { }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="keys">							The keys. </param>
		/// <param name="insertingValues">	The inserting values. </param>
		/// <param name="insertedValues">		The inserted values. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public GridViewInsertEventArgs(IOrderedDictionary keys, IOrderedDictionary insertingValues, IOrderedDictionary insertedValues)
			: this(keys, insertingValues, insertedValues, false) {
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="keys">							The keys. </param>
		/// <param name="insertingValues">	The inserting values. </param>
		/// <param name="insertedValues">		The inserted values. </param>
		/// <param name="cancel">						true to cancel. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public GridViewInsertEventArgs(IOrderedDictionary keys, IOrderedDictionary insertingValues,IOrderedDictionary insertedValues, bool cancel) : base(cancel) {
			Keys = keys;
			if(insertingValues != null) InsertingValues = insertingValues;
			if(insertedValues != null) InsertedValues = insertedValues;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Additional information for grid view ex edit events. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class GridViewExEditEventArgs : GridViewEditEventArgs {
		public IOrderedDictionary Keys;
		public IOrderedDictionary EditValues;
		public GridViewExEditEventArgs(IOrderedDictionary keys, IOrderedDictionary editValues, int newEditIndex)
			: base(newEditIndex) {
			EditValues = editValues;
			Keys = keys;
		}
	}
	public delegate void GridViewPageSizeChangedEventHandler(object sender,GridViewPageSizeChangedEventArgs e);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Additional information for grid view page size changed events. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class GridViewPageSizeChangedEventArgs : EventArgs {
		public int NewPageSize;
		public GridViewPageSizeChangedEventArgs(int NewPageSize){
			this.NewPageSize = NewPageSize;
		}
	}
	#endregion

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Grid view ex. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public partial class GridViewEx :GridView {

		#region Properties
		string _hideColumns = null;
		// PROPERTY:: ViewName

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets the hide columns. </summary>
		///
		/// <value>	The hide columns. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		[Category("Database")]
		[Description("List of columns to hide")]
		public string HideColumns { get {
				if ( _hideColumns == null && !Page.IsPostBack && Common.GetProperty(NamingContainer)+"" != null)
					_hideColumns = Common.GetProperty(NamingContainer) + "";
				return _hideColumns + "";
			}
			set { _hideColumns = value.ToLower(); }
		}
		public IOrderedDictionary InsertBoxes = new OrderedDictionary();
		public IOrderedDictionary FilterBoxes;
		private InsertingRowModeOption _insertingRowMode = InsertingRowModeOption.Footer;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets the inserting row mode. </summary>
		///
		/// <value>	The inserting row mode. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public InsertingRowModeOption InsertingRowMode { get { return _insertingRowMode; } set { _insertingRowMode = value; } }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the inserting row. </summary>
		///
		/// <value>	The inserting row. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public GridViewRow InsertingRow { get { return InsertingRowMode == InsertingRowModeOption.LastRow ? Rows[Rows.Count - 1] : FooterRow; } }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets a value indicating whether this object has data keys. </summary>
		///
		/// <value>	true if this object has data keys, false if not. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool HasDataKeys { get { return DataKeyNames.Length > 0; } }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the edit keys. </summary>
		///
		/// <value>	The edit keys. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public IOrderedDictionary EditKeys { get { return EditIndex < 0 ? new OrderedDictionary() : DataKeys[EditIndex].Values; } }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the edit values. </summary>
		///
		/// <value>	The edit values. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public IOrderedDictionary EditValues {
			get {
				OrderedDictionary values = new OrderedDictionary();
				if(EditIndex >= 0)
					ExtractRowValues(values,Rows[EditIndex],true,true);
				return values;
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the selected values. </summary>
		///
		/// <value>	The selected values. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public IOrderedDictionary SelectedValues {
			get {
				if (SelectedIndex >= Rows.Count) throw new ApplicationException("Selected index in " + this.ClientID + " is greater then number of rows.");
				IOrderedDictionary values = ExtractRowValues(SelectedIndex < 0 ? null : Rows[SelectedIndex]);
				if (SelectedIndex >= 0 && DataKeys != null && DataKeys.Count > SelectedIndex)
					foreach(DictionaryEntry de in DataKeys[SelectedIndex].Values)
						if(!values.Contains(de.Key)) values.Add(de.Key,de.Value);
				return values;
			}
		}
		private DataRow _selectedDataRow = null;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets the selected data row. </summary>
		///
		/// <value>	The selected data row. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public DataRow SelectedDataRow { get { return _selectedDataRow; } set { _selectedDataRow = value; } }
		private bool _showSearch = true;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets a value indicating whether the search is shown. </summary>
		///
		/// <value>	true if show search, false if not. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool ShowSearch { get { return _showSearch; } set { _showSearch = value; } }

		private bool _showDelete = true;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets a value indicating whether the delete is shown. </summary>
		///
		/// <value>	true if show delete, false if not. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool ShowDelete { get { return _showDelete; } set { _showDelete = value; } }

		[Bindable(true)]
		private bool _allowEdit = true;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets a value indicating whether we allow edit. </summary>
		///
		/// <value>	true if allow edit, false if not. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool AllowEdit { get { return _allowEdit; } set { _allowEdit = value; } }

		private bool _gridViewUpdate = true;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets a value indicating whether the grid view update. </summary>
		///
		/// <value>	true if grid view update, false if not. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool GridViewUpdate { get { return _gridViewUpdate; } set { _gridViewUpdate = value; } }

		private string _onSelectClick = "";

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets the on select click. </summary>
		///
		/// <value>	The on select click. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		[Bindable(true)]
		public string OnSelectClick { get { return _onSelectClick; } set { _onSelectClick = value; } }

		private string _onRowClick = null;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets the on row click. </summary>
		///
		/// <value>	The on row click. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		[Bindable(true)]
		public string OnRowClick { get { return _onRowClick == null ? "findUp" : _onRowClick; } set { _onRowClick = value; } }

		private bool _debugMode = false;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets a value indicating whether the debug mode. </summary>
		///
		/// <value>	true if debug mode, false if not. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool DebugMode {
			get {
				string debugModeWebConf = ConfigurationManager.AppSettings["GridViewEx.DebugMode"] + "";
				bool debugMode = false;
				if(debugModeWebConf != "") debugMode = bool.Parse(debugModeWebConf);
				return debugMode || _debugMode;
			}
			set { _debugMode = value; }
		}
		private AObjectDataSourceBase _objectDS = null;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the object ds. </summary>
		///
		/// <value>	The object ds. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected AObjectDataSourceBase ObjectDS {
			get {
				//return _objectDS != null ? _objectDS : _objectDS = Find.ControlByID(Find.ParentControl(this.Parent),DataSourceID) as ObjectDataSourceBase;
				if( _objectDS != null )return _objectDS;
				_objectDS = Parent.FindControl(DataSourceID) as AObjectDataSourceBase;
				if (_objectDS == null)
					_objectDS = this.NamingContainer.FindControl(DataSourceID) as AObjectDataSourceBase;
				if( _objectDS == null )
					try {
						_objectDS = Find.ControlByID(Find.ParentControl(this.Parent),DataSourceID) as AObjectDataSourceBase;
					} catch { }
				return _objectDS;
			}
		}
		LinqDataSource _linqDS = null;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the linq ds. </summary>
		///
		/// <value>	The linq ds. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected LinqDataSource LinqDS {
			get {
				if (_linqDS != null) return _linqDS;
				_linqDS = Parent.FindControl(DataSourceID) as LinqDataSource;
				if (_linqDS == null)
					_linqDS = this.NamingContainer.FindControl(DataSourceID) as LinqDataSource;
				if (_linqDS == null)
					try {
						_linqDS = Find.ControlByID(Find.ParentControl(this.Parent), DataSourceID) as LinqDataSource;
					} catch { }
				return _linqDS;
			}
		}

	
		private int PageSizeCustom = 0;
		public bool FieldsChooserMode = false;
		#endregion

		#region Methods

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Updates the parameters. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public void UpdateParameters() { ObjectDS.SelectParameters.UpdateValues(Context, this); }
		#endregion


		#region State Control

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Override this method to add members that need to be persisted in Control's State. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="values">	. </param>
		///
		/// <example>	values["SelectFilter"] = SelectFilter</example>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected virtual void GetStateValues(Hashtable values) {
			values["LastSearch"] = LastSearch;
			values["PageSizeCustom"] = PageSizeCustom;
			values["SelectedIndex"] = SelectedIndex;
			values["AllowEdit"] = AllowEdit;
			values["ShowDelete"] = ShowDelete;
			values["HideColumns"] = HideColumns;
			values["FieldsChooserMode"] = FieldsChooserMode;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Override this method to get members that have been  persisted in Control's State. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="values">	. </param>
		///
		/// <example>	SelectFilter = values["SelectFilter"] + ""</example>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected virtual void SetStateValues(Hashtable values) {
			LastSearch = values["LastSearch"] + "";
			PageSizeCustom = Convert.ToInt32(values["PageSizeCustom"]);
			SelectedIndex = (int)values["SelectedIndex"];
			AllowEdit = (bool)values["AllowEdit"];
			ShowDelete = (bool)values["ShowDelete"];
			HideColumns = values["HideColumns"] + "";
			FieldsChooserMode = (bool)values["FieldsChooserMode"];
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	
		/// Saves the state of the properties in the <see cref="T:System.Web.UI.WebControls.GridView" />
		/// control that need to be persisted, even when the <see cref="P:
		/// System.Web.UI.Control.EnableViewState" /> property is set to false. 
		/// </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <returns>	
		/// Returns the server control's current view state. If there is no view state associated with the
		/// control, this method returns null. 
		/// </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override object SaveControlState() {
			Hashtable values = new Hashtable();
			GetStateValues(values);
			object state = base.SaveControlState();
			if(state != null) return new Pair(state,values);
			else return values;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	
		/// Loads the state of the properties in the <see cref="T:System.Web.UI.WebControls.GridView" />
		/// control that need to be persisted, even when the <see cref="P:
		/// System.Web.UI.Control.EnableViewState" /> property is set to false. 
		/// </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="savedState">	An <see cref="T:System.Object" /> that contains the saved control
		/// 													state values for the control. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void LoadControlState(object savedState) {
			if(savedState == null) return;
			Hashtable values = new Hashtable();
			if(savedState is Pair) {
				Pair p = savedState as Pair;
				base.LoadControlState(p.First);
				values = p.Second as Hashtable;
			} else if(savedState is Hashtable)
				values = savedState as Hashtable;
			if(values != null) SetStateValues(values);
			else base.LoadControlState(savedState);
		}
		#endregion

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Default constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public GridViewEx() {
			SortList = new SortListClass(this);
		}

		#region Updates

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Inserts a row. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="Exception">	Thrown when exception. </exception>
		/// <exception cref="GridTrace">	Thrown when grid trace. </exception>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public void InsertRow() {
			try {
				if(ObjectDS == null) throw new Exception("No ObjectDataSourse is connected to GridViewEx.");
				IOrderedDictionary insertingValues = InsertBoxes;
				OrderedDictionary keys = new OrderedDictionary(DataKeyNames.Length);
				foreach(string key in DataKeyNames)
					keys.Add(key,DBNull.Value);
				GridViewInsertEventArgs insArgs = new GridViewInsertEventArgs(keys,insertingValues,null);
				if(RowInserting != null) RowInserting(this, insArgs);
				if (insArgs.Cancel) return;
				DataTable insertedTable = ObjectDS.UpdateFields(insertingValues, keys);
				if(RowInserted != null) {
					foreach(string key in DataKeyNames)
						keys[key] = insertedTable.Rows[0][key];
					IOrderedDictionary insertedValues = insArgs.InsertedValues = new OrderedDictionary();
					foreach(DataColumn column in insertedTable.Columns)
						insertedValues.Add(column.ColumnName, insertedTable.Rows[0][column.ColumnName]);
					RowInserted(this, insArgs);
				}
				DataBind();
			} catch(Exception exc) { throw GridTrace(exc); }
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Raises the <see cref="E:System.Web.UI.WebControls.GridView.RowDeleted" /> event. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="SmartException">	Thrown when smart. </exception>
		/// <exception cref="InnerException">	Thrown when inner. </exception>
		///
		/// <param name="e">	A <see cref="T:System.Web.UI.WebControls.GridViewDeletedEventArgs" /> that
		/// 									contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void OnRowDeleted(GridViewDeletedEventArgs e) {
			if(e.Exception != null) {
				TargetInvocationException tie = e.Exception as TargetInvocationException;
				if(tie == null) return;
				e.ExceptionHandled = true;
				SmartException se = new SmartException("DataSourceObject:" + NamingContainer.FindControl(this.DataSourceID).ClientID,tie.InnerException);
				if(Config.UI.DebugMode) throw se;
				else throw tie.InnerException;
			}
			base.OnRowDeleted(e);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Raises the <see cref="E:System.Web.UI.WebControls.GridView.RowUpdating" /> event. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="GridTrace">	Thrown when grid trace. </exception>
		///
		/// <param name="e">	A <see cref="T:System.Web.UI.WebControls.GridViewUpdateEventArgs" /> that
		/// 									contains event data. </param>
		///
		/// ### <exception cref="T:System.Web.HttpException">	There is no handler for the <see cref="E:
		/// 																									System.Web.UI.WebControls.GridView.RowUpdating"
		/// 																									/> event. </exception>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void OnRowUpdating(GridViewUpdateEventArgs e) {
			try {
				base.OnRowUpdating(e);
				if( !GridViewUpdate || e.Cancel) return;
				Control parent = Find.ParentControl(this.Parent) as Control;
				AObjectDataSourceBase odsb = Find.ControlByID(parent,DataSourceID) as AObjectDataSourceBase;
				if(odsb == null) return;

				foreach(object key in e.OldValues.Keys)
					if((e.NewValues[key] + "") == (e.OldValues[key] + ""))
						e.NewValues.Remove(key);

				if (RowUpdating != null) RowUpdating(this, e);
				if (e.Cancel) return;
				if (e.NewValues.Count > 0) odsb.UpdateFields(e.NewValues, e.Keys);

				Rows[e.RowIndex].RowState = Rows[e.RowIndex].RowState & ~DataControlRowState.Edit;
				DataBind();
				e.Cancel = true;
				OnRowUpdated(new GridViewUpdatedEventArgs(-1,null));
			} catch(Exception exc) {
				throw GridTrace(exc);
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Raises the <see cref="E:System.Web.UI.WebControls.GridView.RowEditing" /> event. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="e">	A <see cref="T:System.Web.UI.WebControls.GridViewEditEventArgs" /> that
		/// 									contains event data. </param>
		///
		/// ### <exception cref="T:System.Web.HttpException">	There is no handler for the <see cref="E:
		/// 																									System.Web.UI.WebControls.GridView.RowEditing"
		/// 																									/> event. </exception>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void OnRowEditing(GridViewEditEventArgs e) {
			//foreach(GridViewRow row in Rows)
			if(EditIndex >= 0) {
				UpdateRow(EditIndex, false);
				e.NewEditIndex = -1;
				///SelectedIndex = -1;
			} else {
				///SelectedIndex = -1;
				base.OnRowEditing(e);
				if( RowEditingEx != null )
					RowEditingEx(this,new GridViewExEditEventArgs(DataKeys[e.NewEditIndex].Values,ExtractRowValues(Rows[e.NewEditIndex]),e.NewEditIndex));
			}
		}
		#endregion

		#region Overrides

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	
		/// Gets or sets a value indicating whether the <see cref="M:
		/// System.Web.UI.WebControls.BaseDataBoundControl.DataBind" /> method should be called. 
		/// </summary>
		///
		/// <value>	true if requires data binding, false if not. </value>
		///
		/// ### <returns>	
		/// The returned value is true if the data-bound control's <see cref="M:
		/// System.Web.UI.WebControls.BaseDataBoundControl.DataBind" /> method should be called before the
		/// control is rendered; otherwise, the value is false. 
		/// </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public new bool RequiresDataBinding { get { return base.RequiresDataBinding; } set { base.RequiresDataBinding = value; } }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	
		/// Gets or sets the number of records to display on a page in a <see cref="T:
		/// System.Web.UI.WebControls.GridView" /> control. 
		/// </summary>
		///
		/// <value>	The size of the page. </value>
		///
		/// ### <returns>	The number of records to display on a single page. The default is 10. </returns>
		/// ### <exception cref="T:System.ArgumentOutOfRangeException">	The <see cref="P:
		/// 																														System.Web.UI.WebControls.GridView.Pa
		/// 																														geSize" /> property is set to a
		/// 																														value less than 1. </exception>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public override int PageSize {
			get {
				return PageSizeCustom > 0 ? PageSizeCustom : base.PageSize;
			}
			set {
				if (value > 0) {
					base.PageSize = PageSizeCustom = value;
					RequiresDataBinding = true;
				}
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Raises the <see cref="E:System.Web.UI.Control.Init" /> event. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="e">	An <see cref="T:System.EventArgs" /> that contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void OnInit(EventArgs e) {
			base.OnInit(e);
			Page.RegisterRequiresControlState(this);
			TemplateField templ = new TemplateField();
			//if( FieldsChooserMode )
				templ.HeaderTemplate = Page.LoadTemplate("~/Controls/FirstColumn/FieldChooserHeader.ascx");
			templ.ItemTemplate = Page.LoadTemplate("~/Controls/FirstColumn/Item" /*+ addOn*/ + ".ascx");
			if( AllowEdit )templ.EditItemTemplate = Page.LoadTemplate("~/Controls/FirstColumn/Edit.ascx");
			if(ShowFooter) templ.FooterTemplate = Page.LoadTemplate("~/Controls/FirstColumn/Footer.ascx");
			Columns.Insert(0,templ);
			Page.LoadComplete += new EventHandler(Page_LoadComplete);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Event handler. Called by Page for load complete events. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="sender">	Source of the event. </param>
		/// <param name="e">			An <see cref="T:System.EventArgs" /> that contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		void Page_LoadComplete(object sender, EventArgs e) {
			if( ObjectDS != null )
				ObjectDS.Selected += new ObjectDataSourceStatusEventHandler(ObjectDS_Selected);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Raises the load event. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="Exception">	Thrown when exception. </exception>
		///
		/// <param name="e">	An <see cref="T:System.EventArgs" /> that contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void OnLoad(EventArgs e) {
			try {
				base.OnLoad(e);

				#region Pager
				PagerSettings.Mode = PagerButtons.NumericFirstLast;
				PagerSettings.FirstPageText = "First";
				PagerSettings.LastPageText = "Last";
				PagerSettings.NextPageText = "Next";
				PagerSettings.PreviousPageText = "Prev";
				PagerStyle.CssClass = "Pager"; 
				#endregion

				#region Register JScript
				new JS().RegisterScripts(Page);
				#endregion

				if (Page.IsPostBack) {
					#region Search
					InsertBoxes = ExtractRowValues(InsertingRow);
					FilterBoxes = FindInsertRowBoxes(HeaderRow);
					if (/*BuildFilter() == "" &&*/ LastSearch != "")
						Search_TextChanged(null, null);// To handle empty-textbox-no-event bug 
					#endregion
				}
			} catch (Exception exc) {
				throw new Exception(SmartException.GetMessages(exc)+" @ "+this.NamingContainer.ClientID, exc);
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Event handler. Called by ObjectDS for selected events. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="sender">	Source of the event. </param>
		/// <param name="e">			A <see cref="T:System.Web.UI.WebControls.GridViewDeletedEventArgs" />
		/// 											that contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		void ObjectDS_Selected(object sender, ObjectDataSourceStatusEventArgs e) {
			DataView dv = (e.ReturnValue as DataView);
			SelectedDataRow = dv == null || SelectedIndex < 0 || SelectedIndex >= dv.Count ? null : dv[SelectedIndex].Row;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Raises the <see cref="E:System.Web.UI.WebControls.GridView.RowCommand" /> event. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="e">	A <see cref="T:System.Web.UI.WebControls.GridViewCommandEventArgs" /> that
		/// 									contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void OnRowCommand(GridViewCommandEventArgs e) {
			base.OnRowCommand(e);
			if(e.CommandName.ToLower() != "edit") {
				if(EditIndex >= 0) EditIndex = -1;
				return;
			}
			///SelectedIndex = -1;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	
		/// Gets or sets the index of the selected row in a <see cref="T:
		/// System.Web.UI.WebControls.GridView" /> control. 
		/// </summary>
		///
		/// <value>	The selected index. </value>
		///
		/// ### <returns>	
		/// The zero-based index of the selected row in a <see cref="T:System.Web.UI.WebControls.GridView"
		/// /> control. The default is -1, which indicates that no row is currently selected. 
		/// </returns>
		/// ### <exception cref="T:System.ArgumentOutOfRangeException">	The <see cref="P:
		/// 																														System.Web.UI.WebControls.GridView.Se
		/// 																														lectedIndex" /> property is set to
		/// 																														a value less than -1. </exception>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public override int SelectedIndex {
			get {
				return base.SelectedIndex;
			}
			set {
				if(base.SelectedIndex == value) return;
				base.SelectedIndex = value;
				if (value == -1) OnSelectedIndexChanged(new EventArgs());
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets the index of the row to edit. </summary>
		///
		/// <value>	The edit index. </value>
		///
		/// ### <returns>	
		/// The zero-based index of the row to edit. The default is -1, which indicates that no row is
		/// being edited. 
		/// </returns>
		/// ### <exception cref="T:System.ArgumentOutOfRangeException">	The specified index is less than -
		/// 																														1. </exception>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public override int EditIndex {
			get {
				return base.EditIndex;
			}
			set {
				if(base.EditIndex == value) return;
				base.EditIndex = value;
				OnSelectedIndexChanged(new EventArgs());
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	
		/// Raises the <see cref="E:System.Web.UI.WebControls.GridView.SelectedIndexChanging" /> event. 
		/// </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="e">	A <see cref="T:System.Web.UI.WebControls.GridViewSelectEventArgs" /> that
		/// 									contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void OnSelectedIndexChanging(GridViewSelectEventArgs e) {
			base.OnSelectedIndexChanging(e);
			if(SelectedRow != null) SetSelected(SelectedRow,true);
			if(e.Cancel = e.NewSelectedIndex == SelectedIndex) {
				if(SelectedIndexUnSelected != null) SelectedIndexUnSelected(this,new GridViewSelectEventArgs(-1));
				SelectedIndex = -1;
				//if(AllowEdit) {
				//	EditIndex = e.NewSelectedIndex;
				//	Rows[EditIndex].RowState = DataControlRowState.Edit;
				//}
				return;
			}
			if(DataKeys.Count > 0 && DataKeys[e.NewSelectedIndex][0] == DBNull.Value) {
				EditIndex = e.NewSelectedIndex;
				Rows[EditIndex].RowState |= DataControlRowState.Edit;
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	
		/// Retrieves the values of each field declared within the specified row and stores them in the
		/// specified <see cref="T:System.Collections.Specialized.IOrderedDictionary" /> object. 
		/// </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="fieldValues">						An <see cref="T:
		/// 																			System.Collections.Specialized.IOrderedDictionary" />
		/// 																			used to store the field values. </param>
		/// <param name="row">										The <see cref="T:System.Web.UI.WebControls.GridViewRow"
		/// 																			/> from which to retrieve the field values. </param>
		/// <param name="includeReadOnlyFields">	true to include read-only fields; otherwise, false. </param>
		/// <param name="includePrimaryKey">			true to include the primary key field or fields;
		/// 																			otherwise, false. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void ExtractRowValues(System.Collections.Specialized.IOrderedDictionary fieldValues,GridViewRow row,bool includeReadOnlyFields,bool includePrimaryKey) {
			base.ExtractRowValues(fieldValues,row,includeReadOnlyFields,includePrimaryKey);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Raises the <see cref="E:System.Web.UI.WebControls.GridView.Sorted" /> event. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="e">	An <see cref="T:System.EventArgs" /> that contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void OnSorted(EventArgs e) {
			base.OnSorted(e);
			SelectedIndex = -1;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Creates the child controls. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void CreateChildControls() {
			base.CreateChildControls();
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Creates the child controls. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="dataSource">		The data source. </param>
		/// <param name="dataBinding">	true to data binding. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override int CreateChildControls(IEnumerable dataSource,bool dataBinding) {
			return base.CreateChildControls(dataSource,dataBinding);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Ensures that child controls. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="GridTrace">	Thrown when grid trace. </exception>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void EnsureChildControls() {
			try {
				if(EnsureDS != null) EnsureDS(this, new EventArgs());
				base.EnsureChildControls();
			} catch(Exception exc) {
				throw GridTrace(exc);
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Dictionary of data control fields. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		class DataControlFieldDictionary : Dimok.Dictionary.CaseInsensitiveStringDictionary<DataControlField> { }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the data columns. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		DataControlFieldDictionary DataColumns() {
				DataControlFieldDictionary od = new DataControlFieldDictionary();
				foreach (DataControlField col in Columns)
					od[col.SortExpression] = col;
				return od;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Raises the <see cref="E:System.Web.UI.Control.PreRender" /> event. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="GridTrace">	Thrown when grid trace. </exception>
		///
		/// <param name="e">	An <see cref="T:System.EventArgs" /> that contains the event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void OnPreRender(EventArgs e) {
			try {
				bool rdb = RequiresDataBinding;
				DataControlFieldDictionary dcfd = DataColumns();
				foreach (DataControlField col in Columns)
					col.Visible = true;// col.Visible;

				if (!FieldsChooserMode && HideColumns != "")
					foreach (string hideColumn in HideColumns.Split(','))
							if( dcfd.ContainsKey(hideColumn) )dcfd[hideColumn].Visible = false;
				RequiresDataBinding = rdb;
				base.OnPreRender(e);
			} catch (Exception exc) { throw GridTrace(exc); }
			Visible = true;
			//if(CssClass == "")
			CssClass = "GridView "+CssClass;
			SelectedRowStyle.BackColor = System.Drawing.Color.LightCyan;
			HeaderStyle.VerticalAlign = VerticalAlign.Bottom;
			if(FooterRow != null) {
				FooterStyle.HorizontalAlign = HorizontalAlign.Center;
				FooterStyle.CssClass = "Footer";
			}
			//RowStyle.CssClass = "GV_Row " + RowStyle.CssClass;
			if(SelectedIndex == -1) return;
			try {
				GridViewRow gvr = SelectedRow;
			} catch { SelectedIndex = -1; return; }
			SetSelected(SelectedRow,false);
			PagerSettings.PropertyChanged += new EventHandler(PagerSettings_PropertyChanged);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Raises the <see cref="E:System.Web.UI.WebControls.GridView.RowCreated" /> event. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="e">	A <see cref="T:System.Web.UI.WebControls.GridViewRowEventArgs" /> that
		/// 									contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void OnRowCreated(GridViewRowEventArgs e) {
			base.OnRowCreated(e);
			switch(e.Row.RowType) {
				case DataControlRowType.Header:
					InitFilter(e.Row);
					break;
				case DataControlRowType.Footer:
					e.Row.DataBinding += new EventHandler(Footer_DataBinding);
					TableCellCollection cells = e.Row.Cells;
					for(int i = 0; i < cells.Count - 1; i++)
						for(int j = cells[i].ColumnSpan - 1; j > 0; j++)
							cells.Remove(cells[i + j]);
					break;
				case DataControlRowType.DataRow:
					//e.Row.Cells[0].Attributes.Add("onclick","event.cancelBubble=true;");
					if((e.Row.RowState & DataControlRowState.Edit) != DataControlRowState.Edit) {
						if(!AllowEdit) {
							WebControl c = e.Row.Cells[0].Controls[0].FindControl("goEdit") as WebControl;
							if(c != null) c.Visible = false;
							c = e.Row.Cells[0].Controls[0].FindControl("goSelect") as WebControl;
							if(!ShowFooter && c != null) c.Style.Add("display","none");
						}
						if(!ShowDelete) {
							Control c = e.Row.Cells[0].Controls[0].FindControl("DEL");
							if(c != null) c.Visible = false;
						}
					}
					if(IsRowInState(e.Row,DataControlRowState.Normal,DataControlRowState.Alternate,DataControlRowState.Selected) &&
						 !IsRowInState(e.Row,DataControlRowState.Edit)
						) {
						for(int i = 1; i < e.Row.Cells.Count; i++) {
							if(_onRowClick != null) {
								if (_onRowClick == "void") continue;
								string functionName = Regex.Match(OnRowClick,"^[^(]+").Value;
								string[] functionParams = Regex.Match(OnRowClick,"[(]([^()]*)[)]").Groups[1].Value.Split(',');
								List<string> lstParams = new List<string>();
								foreach(string param in functionParams)
									if(param != "") {
										DataRowView drv = e.Row.DataItem as DataRowView;
										if(drv == null) continue;
										lstParams.Add("\"" + (drv[param] + "").Replace("\"","\\\"") + "\"");
									}
								string onClick = "if(!event.altKey && !event.ctrlKey)" + functionName + "(" + string.Join(",", lstParams.ToArray()) + ")";
								WebControl c = e.Row.Cells[0].Controls[0].FindControl("goSelect") as WebControl;
								if(c != null) {
									string postBackUrl = ((IButtonControl)c).PostBackUrl;
									//if(!(c.Attributes["onclick"] + "").Contains(onClick))
									//	c.Attributes["onclick"] = onClick + ";" + c.Attributes["onclick"];
									e.Row.Cells[i].Attributes.Add("onclick",onClick + ";findUp(this,'goSelect','goEdit')" + postBackUrl);
								} else e.Row.Cells[i].Attributes.Add("onclick", onClick + ";findUp(this,'goSelect','goEdit')");
								e.Row.Cells[i].Style.Add("cursor", "hand");
							} else
								e.Row.Cells[i].Attributes.Add("onclick", OnRowClick + "(this,'goSelect','goEdit')");
								//e.Row.Cells[i].Attributes.Add("onclick", OnRowClick + "(this,'goSelect','goEdit')");
							
						}
						e.Row.DataBinding += new EventHandler(Row_DataBinding);
					}
					break;
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Initializes the pager row displayed when the paging feature is enabled. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="row">							A <see cref="T:System.Web.UI.WebControls.GridViewRow" /> that
		/// 																represents the pager row to initialize. </param>
		/// <param name="columnSpan">				The number of columns the pager row should span. </param>
		/// <param name="pagedDataSource">	A <see cref="T:System.Web.UI.WebControls.PagedDataSource" />
		/// 																that represents the data source. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void InitializePager(GridViewRow row,int columnSpan,PagedDataSource pagedDataSource) {
			base.InitializePager(row,columnSpan,pagedDataSource);
			TableCell cell = row.Cells[0];
			Literal ltr = new Literal();
			ltr.Text = ". <b>" + pagedDataSource.DataSourceCount+ "</b> Rows. ";

			TableCell totalCell = new TableCell();
			totalCell.Controls.Add(ltr);
			TextBox tbRowsPerPage = new TextBox();
			tbRowsPerPage.ID = "txtRowsPerPage";
			tbRowsPerPage.TextChanged += new EventHandler(tbRowsPerPage_TextChanged);
			tbRowsPerPage.Columns = 2;
			tbRowsPerPage.Style.Add("width", "auto");
			tbRowsPerPage.Style.Add( HtmlTextWriterStyle.TextAlign, "right");
			tbRowsPerPage.AutoPostBack = true;
			tbRowsPerPage.Text = PageSize + "";
			totalCell.Controls.Add(tbRowsPerPage);
			ltr = new Literal();
			ltr.Text = " rows/pg.";
			totalCell.Controls.Add(ltr);

			if (pagedDataSource.PageCount == 1 && pagedDataSource.Count > 1) {
				GridViewRow pgRow = new GridViewRow(-1, -1, DataControlRowType.Pager, DataControlRowState.Normal);
				pgRow.Cells.Add(totalCell);
				totalCell.ColumnSpan = columnSpan;
				((System.Web.UI.WebControls.Table)(FooterRow.Parent)).Rows.Add(pgRow);
			} else {
				TableRow pageRow = cell.Controls[0].Controls[0] as TableRow;
				pageRow.Cells.Add(totalCell);
			}		
			this.PagerSettings.Visible = true;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets the sort expression associated with the column or columns being sorted. </summary>
		///
		/// <value>	The sort expression. </value>
		///
		/// ### <returns>	The sort expression associated with the column or columns being sorted. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public new string SortExpression {
			get {
				//new System.Web.UI.WebControls.LinqDataSource().OrderByParameters
				return base.SortExpression != "" ? base.SortExpression :
					ObjectDS == null ? LinqDS.OrderBy : ObjectDS.OrderBys.Replace("ORDER BY", "").Trim();
			}
			set {
				ObjectDS.OrderBy.Add(value);
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Sort list class. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public class SortListClass {
			GridViewEx gv;
			public SortListClass(GridViewEx gv) { this.gv = gv; }
			public string Add(string expression) { return Add(expression, gv.SortExpression); }
			public static string Add(string expression,string SortExpression) {
				if( !expression.StartsWith("["))expression = "["+expression+"]";
				List<string> lst = new List<string>(SortExpression.Split(','));
				lst.Remove("");
				for(int i = 0; i < lst.Count; i++)
					if(!lst[i].StartsWith(expression.Split(']')[0])) continue;
					else {
						string sort = lst[i];
						if(i == 0) {
							if(Regex.IsMatch(sort," DESC$")) lst[i] = Regex.Replace(sort," DESC$"," ASC");
							else if(Regex.IsMatch(sort," ASC$")) lst[i] = Regex.Replace(sort," ASC$"," DESC");
							else lst[i] = sort + " DESC";
						} else {
							lst.RemoveAt(i);
							lst.Insert(0,expression);
						}
						return string.Join(",",lst.ToArray());
					}
				//if(lst[0] == "") lst.RemoveAt(0);
				lst.Insert(0,expression);
				return string.Join(",",lst.ToArray());
			}
			public Dictionary<string, bool> SortDictionary() {
				Dictionary<string,bool> dic = new Dictionary<string,bool>();
				foreach (string sort in gv.SortExpression.Split(',')) {
					Match m = Regex.Match(sort, @"\[(?<field>.*)\]\s*(?<dir>\s+DESC)?", RegexOptions.RightToLeft);
					dic[m.Groups["field"].Value] = m.Groups["dir"].Value != "";
				}
				return dic;
			}
			public int SortDictionaryPos(string FieldName) {
				Dictionary<string, bool> dic = SortDictionary();
				int k = 0;
				foreach (string key in dic.Keys)
					if (key == FieldName) break;
					else k++;
				return ++k;
			}
		}
		SortListClass SortList;
		bool _allowUserSorting = true;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets a value indicating whether we allow user sorting. </summary>
		///
		/// <value>	true if allow user sorting, false if not. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool AllowUserSorting { get { return _allowUserSorting; } set { _allowUserSorting = value; } }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Raises the <see cref="E:System.Web.UI.WebControls.GridView.Sorting" /> event. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="e">	A <see cref="T:System.Web.UI.WebControls.GridViewSortEventArgs" /> that
		/// 									contains event data. </param>
		///
		/// ### <exception cref="T:System.Web.HttpException">	There is no handler for the <see cref="E:
		/// 																									System.Web.UI.WebControls.GridView.Sorting"
		/// 																									/> event. </exception>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected override void OnSorting(GridViewSortEventArgs e) {
			if (AllowUserSorting) {
				e.SortExpression = SortList.Add(e.SortExpression);
				e.SortDirection = SortDirection.Ascending;
			} else e.Cancel = true;
			base.OnSorting(e);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Query if 'row' is row in state. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="row">		The <see cref="T:System.Web.UI.WebControls.GridViewRow" /> from which to
		/// 											retrieve the field values. </param>
		/// <param name="states">	A variable-length parameters list containing states. </param>
		///
		/// <returns>	true if row in state, false if not. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private bool IsRowInState(GridViewRow row,params DataControlRowState []states) {
			foreach(DataControlRowState state in states)
				if((row.RowState & state) == state) return true;
			return false;
		}
		#endregion

		#region Events

		public event EventHandler EnsureDS;
		public event EventHandler<GridViewSelectEventArgs> SelectedIndexUnSelected;
		public event EventHandler<GridViewInsertEventArgs> RowInserting;
		public event EventHandler<GridViewInsertEventArgs> RowInserted;
		public event EventHandler<GridViewExEditEventArgs> RowEditingEx;
		public event EventHandler<GridViewUpdateEventArgs> RowUpdating;
		public event GridViewPageSizeChangedEventHandler PageSizeChanged;

		#region SearchChangedEventHandler
		public delegate void SearchChangedEventHandler(string search);
		private SearchChangedEventHandler SearchChangedEvent;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	 the search changed. </summary>
		///
		/// <value>	The search changed. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public event SearchChangedEventHandler SearchChanged {
			add {
				SearchChangedEvent = (SearchChangedEventHandler)System.Delegate.Combine(SearchChangedEvent,value);
			}
			remove {
				SearchChangedEvent = (SearchChangedEventHandler)System.Delegate.Remove(SearchChangedEvent,value);
			}
		}

		#endregion

		#endregion

		#region Filter

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Sets a filter boxes. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="fields">	The fields. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public void SetFilterBoxes(IDictionary fields) {
			GridViewRow rowHeader = HeaderRow;
			FilterBoxes = new OrderedDictionaryEx(true);
			if( fields != null)
				foreach (string key in fields.Keys) {
					string fieldName = Regex.Match(key.Trim(), @"^\[?(.+?)\]?$").Groups[1].Value;
					FilterBoxes.Add(fieldName, fields[key]);
					HideColumns_REM(fieldName.ToLower());
				}
			//FilterBoxes.Add(key, Regex.Match(fields[key] + "", "^'?(.+?)'?$").Groups[1].Value);
			SetRowBoxes(rowHeader,FilterBoxes);
		}
		DataTable _headerTotals;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets the header totals. </summary>
		///
		/// <value>	The header totals. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public DataTable HeaderTotals {
			get { return _headerTotals; }
			set {
				if (value.Rows.Count == 0)
					foreach (DataColumn col in value.Columns)
						col.DataType = typeof(string);
				_headerTotals = value;
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Initialises the filter. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="GridTrace">	Thrown when grid trace. </exception>
		///
		/// <param name="row">	The <see cref="T:System.Web.UI.WebControls.GridViewRow" /> from which to
		/// 										retrieve the field values. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private void InitFilter(GridViewRow row) {
			if (!FieldsChooserMode) row.Cells[0].Controls.RemoveAt(0);
			if (!ShowSearch) return;
			try {
				List<string> lstHideColumns = new List<string>(HideColumns.Split(','));
				lstHideColumns.Remove("");
				row.CssClass += " Header";
				Dictionary<string, bool> dicSort = SortList.SortDictionary();
				for (int i = 1; i < row.Cells.Count; i++) {
					TableCell cell = row.Cells[i];
					DataControlFieldCell dcCell = (DataControlFieldCell)cell;
					dcCell.ContainingField.HeaderText = dcCell.ContainingField.HeaderText.MakeHeader();
					string ID = dcCell.ContainingField.SortExpression;
					bool isHidden = lstHideColumns.Contains(ID.ToLower());
					if (dicSort.ContainsKey(ID)) {
						Image srt = new Image();
						int pos = SortList.SortDictionaryPos(ID);
						srt.ImageUrl = "/images/buttons/"+(dicSort[ID] ? "DESC" : "ASC")+Math.Min(4,pos)+".gif";
						dcCell.Controls.Add(srt);
					}
					Literal br = new Literal();
					br.Text = "<BR>";
					TextBox tb = new TextBox();
					tb.ID = ID;
					if ((tb.ID + "") == "") continue;// throw new Exception("SortExpression must be set for column " + Columns[i].HeaderText);
					tb.CssClass = "Filter";
					tb.AutoPostBack = true;
					dcCell.HorizontalAlign = HorizontalAlign.Center;
					tb.TextChanged += new EventHandler(Search_TextChanged);
					if (FilterBoxes != null) tb.Text = FilterBoxes[tb.ID] + "";
//          tb.ToolTip = (tb.Text != "" ? tb.Text + Environment.NewLine : "") + @"[ALT]/click to open Filter Builder or
//=  XXX - value equals XXX
//>  XXX - value is greater then XXX
//<  XXX - value is less then XXX
//<> XXX - value is not equal XXX
//>< XXX AND YYY - value is BETWEEN XXX AND YYY
//== XXX,YYY,ZZZ - value equals XXX or YYY or ZZZ";
					try {
						string sql = Page.Server.UrlEncode("SELECT " + ObjectDS.SelectList + " FROM " + ObjectDS.SelectViewName).Replace("'", "\\'");
						string filterString = Page.Server.UrlEncode(LastSearch).Replace("'", "\\'");
						tb.Attributes["onclick"] = "GVX_showFilter('" + sql + "','" + filterString + "')";
					} catch { }
					if (FieldsChooserMode) {
						dcCell.Controls.RemoveAt(0);
						CheckBox chk = new CheckBox();
						chk.ID = "chkVisible_" + ID;
						chk.InputAttributes["Field"] = ID;
						chk.Text = dcCell.ContainingField.HeaderText;
						if( tb.Text != "" )chk.Attributes["disabled"] = "true";
						chk.CssClass = "CheckBox";
						chk.CheckedChanged += new EventHandler(FieldVisible_CheckedChanged);
						chk.Checked = !isHidden;
						dcCell.Controls.AddAt(0,chk);
					} //else if (isHidden && tb.Text == "") {
						////dcCell.ContainingField.t.Visible = false;
						//continue;
					//}
					//dcCell.ContainingField.Visible = true;
					if (tb.Text != "") HideColumns_REM(ID);
					dcCell.Controls.Add(br);
					dcCell.Controls.Add(tb);
					if (ID != "" && HeaderTotals != null)
						foreach (DataRow hdrRow in HeaderTotals.Rows)
							if (hdrRow[ID] + "" != "") {
								Literal ltr = new Literal();
								ltr.Text = "<br>" + hdrRow[ID];
								dcCell.Controls.Add(ltr);
								row.Style["vertical-align"] = "top";
							}
				}
			} catch (Exception exc) {
				throw GridTrace(exc);
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Hides the columns adjust. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="column">	The column. </param>
		/// <param name="remove">	true to remove. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		void HideColumns_Adjust(string column,bool remove) {
			if (remove) HideColumns_REM(column);
			else HideColumns_ADD(column);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Hides the columns add. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="column">	The column. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		void HideColumns_ADD(string column) {
			List<string> lst = new List<string>(HideColumns.Split(','));
			lst.Remove("");
			if (lst.Contains(column.ToLower())) return;
			lst.Add(column.ToLower());
			HideColumns = string.Join(",", lst.ToArray());
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Hides the columns rem. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="column">	The column. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		void HideColumns_REM(string column) {
			List<string> lst = new List<string>(HideColumns.Split(','));
			lst.Remove("");
			lst.Remove(column.ToLower());
			HideColumns = string.Join(",", lst.ToArray());
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Event handler. Called by FieldVisible for checked changed events. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="sender">	Source of the event. </param>
		/// <param name="e">			An <see cref="T:System.EventArgs" /> that contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		void FieldVisible_CheckedChanged(object sender, EventArgs e) {
			CheckBox chk = (CheckBox)sender;
			string column = chk.InputAttributes["Field"]+"";
			if (column != "") HideColumns_Adjust(column, chk.Checked);
		}

		private string _lastSearch = "";

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets or sets the last search. </summary>
		///
		/// <value>	The last search. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string LastSearch { get { return _lastSearch; } set { _lastSearch = value; } }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Event handler. Called by Search for text changed events. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="sender">	Source of the event. </param>
		/// <param name="e">			An <see cref="T:System.EventArgs" /> that contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		void Search_TextChanged(object sender,EventArgs e) {
			string newSearch = BuildFilter();
			if (LastSearch.ToLower() != newSearch.ToLower()) {
				RequiresDataBinding = true;
				LastSearch = newSearch;
				SelectedIndex = -1;
				if (SearchChangedEvent != null)
					SearchChangedEvent(newSearch);
			}
			if (newSearch != "")
				if (ObjectDS != null) ObjectDS.Where.Add(newSearch);
				else if (LinqDS != null)
					LinqDS.Where = BuildFilter(true); 
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Builds the filter. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string BuildFilter() { return BuildFilter(false); }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Builds the filter. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="GridTrace">	Thrown when grid trace. </exception>
		///
		/// <param name="IsLinq">	true if is linq. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string BuildFilter(bool IsLinq) {
			try {
				List<string> arlFilter = new List<string>();
				FilterBoxes = new OrderedDictionary();
				for(int i = 1; ShowSearch && HeaderRow != null && i <= HeaderRow.Controls.Count - 1; i++) {
					TextBox tb = Find.ChildByType(HeaderRow.Controls[i], typeof(TextBox)) as TextBox;
					if(tb == null) continue;
					GridViewSearch.SearchTypes searchType = GridViewSearch.SearchTypes.None;
					if (IsLinq) searchType |= GridViewSearch.SearchTypes.Linq;
					string text = tb.Text;
					string fieldName = tb.ID;
					if(text != "") {
						arlFilter.Add(GridViewSearch.BuildFilter(fieldName,text,searchType));
					}
					FilterBoxes[tb.ID] = text;
				}
				return SQLHelperEx.CheckInjects(string.Join(" AND ", arlFilter.ToArray()));
			} catch(Exception ex) {
				throw GridTrace(ex);
			}
		}

		#endregion

		#region Event Handlers

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Event handler. Called by PagerSettings for property changed events. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="sender">	Source of the event. </param>
		/// <param name="e">			An <see cref="T:System.EventArgs" /> that contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		void PagerSettings_PropertyChanged(object sender,EventArgs e) {
			bool visible = ((PagerSettings)sender).Visible;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Event handler. Called by tbRowsPerPage for text changed events. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="sender">	Source of the event. </param>
		/// <param name="e">			An <see cref="T:System.EventArgs" /> that contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		void tbRowsPerPage_TextChanged(object sender,EventArgs e) {
			PageSizeCustom = Convert.ToInt32("0" + ((ITextControl)sender).Text);
			if (PageSizeChanged != null) PageSizeChanged(this, new GridViewPageSizeChangedEventArgs(PageSizeCustom));
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Event handler. Called by Row for data binding events. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="sender">	Source of the event. </param>
		/// <param name="e">			An <see cref="T:System.EventArgs" /> that contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		void Row_DataBinding(object sender,EventArgs e) {
			if(OnSelectClick == "") return;
			GridViewRow gvr = (GridViewRow)sender;
			DataRowView drv = gvr.DataItem as DataRowView;
			List<string> lst = new List<string>();
			foreach(string key in DataKeyNames)
				lst.Add("\"" + drv.Row[key].ToString().Replace("\"","\\\"") + "\"");
			Button selectButton = gvr.Cells[0].Controls[0].FindControl("goSelect") as Button;
			if(selectButton != null) {
				selectButton.Attributes.Add("OnDblClick",OnSelectClick + string.Join(",",lst.ToArray()) + ");");
				selectButton.ToolTip += Environment.NewLine + "<DoubleClick> to see details";
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Event handler. Called by Footer for data binding events. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="GridTrace">	Thrown when grid trace. </exception>
		///
		/// <param name="sender">	Source of the event. </param>
		/// <param name="e">			An <see cref="T:System.EventArgs" /> that contains event data. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		void Footer_DataBinding(object sender,EventArgs e) {
			GridViewRow row = (GridViewRow)sender;
			try {
				foreach(TableCell cell in row.Cells) {
					TextBox tb = Find.ChildTextBox(cell);
					if(tb != null && InsertBoxes.Contains(tb.ID)) tb.Text = InsertBoxes[tb.ID] + "";
					DropDownList sel = Find.ChildDropDownList(cell);
					if(sel != null && InsertBoxes.Contains(sel.ID)) sel.SelectedValue = InsertBoxes[sel.ID] + "";
				}
			} catch(Exception exc) {
				throw GridTrace(exc);
			}
		}
		#endregion

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Searches for the first row. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="dataKey">	The data key. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public void FindRow(IOrderedDictionary dataKey) {
			int rowIndex = -1;
			foreach(DataKey rowKey in DataKeys) {
				rowIndex++;
				for(int keyIndex = 0; keyIndex < dataKey.Count; keyIndex++)
					if(dataKey[keyIndex].ToString() != rowKey[keyIndex].ToString()) break;
					else if(keyIndex == dataKey.Count - 1) {
						SelectedIndex = rowIndex;
						break;
					}
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Grid trace. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="exc">	The exc. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected Exception GridTrace(Exception exc) {
			List<string> message = new List<string>();
			message.Add("ID:" + ClientID + " @ " + NamingContainer.TemplateControl.AppRelativeVirtualPath);
			if (ObjectDS != null) message.Add("ConnectionString:" + ObjectDS.ConnectionString);
			message.Add(SmartException.GetMessages(exc));
			Exception newExc = new Exception(string.Join(Environment.NewLine,message.ToArray()),exc);
			ClientException.Publish(newExc);
			return DebugMode ? newExc : exc;
		}
	}


	#region Enums

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Values that represent InsertingRowModeOption. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public enum InsertingRowModeOption { Footer, LastRow }
	#endregion

}
